/*
 *  Copyright 2012 Iurii Dunko, Iana Potochniak.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.yyminijava.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.opu.yyminijava.Part;
import org.opu.yyminijava.syntaxtree.ArithmeticalAddExpression;
import org.opu.yyminijava.syntaxtree.ArithmeticalAddOp;
import org.opu.yyminijava.syntaxtree.ArithmeticalMullExpression;
import org.opu.yyminijava.syntaxtree.ArithmeticalMullOp;
import org.opu.yyminijava.syntaxtree.BooleanExpression;
import org.opu.yyminijava.syntaxtree.BooleanOp;
import org.opu.yyminijava.syntaxtree.CompareExpression;
import org.opu.yyminijava.syntaxtree.CompareOp;
import org.opu.yyminijava.syntaxtree.Expression;
import org.opu.yyminijava.syntaxtree.LogicalExpression;
import org.opu.yyminijava.syntaxtree.LogicalOp;
import org.opu.yyminijava.syntaxtree.PostfixExpression;
import org.opu.yyminijava.syntaxtree.PrefixExpression;
import org.opu.yyminijava.syntaxtree.PrefixOp;
import static org.opu.yyminijava.util.MathUtil.PostfixNotationType.*;

/**
 *
 * @author Iurii Dunko
 * @author Iana Potochniak
 */
public class MathUtil {

    public static enum PostfixNotationType {
        MATH_OPERATION,
        POSTFIX_EXPRESSION
    }

    public static final class PostfixNotationElement {
        public final PostfixNotationType type;
        public final Object value;

        public PostfixNotationElement(PostfixNotationType type, Object value) {
            this.type = type;
            this.value = value;
        }

        @Override
        public String toString() {
            return String.valueOf(value) ;//+ "(" + type + ")";
        }
    }

    /**
     * Parse expression and define set of it element in postfix notation.
     * @param exp expression that will be represented in postgex notation
     * @return array of data in postfix notation
     */
    public static PostfixNotationElement[] toPostfixNotation(Expression exp){
        List<PostfixNotationElement> elements = parseBooleanExpression(exp.getBoolExp());

        return elements.toArray(new PostfixNotationElement[elements.size()]);
    }

    @Deprecated
    private static void addOperations(List<PostfixNotationElement> elements,
            List<PostfixNotationElement> operations){
        Collections.reverse(operations);
        elements.addAll(operations);
    }

    // TODO: Iurii Dunko: Remove copy/paste in parse methods !!!

    private static List<PostfixNotationElement> parseBooleanExpression(BooleanExpression boolExp){
        List<PostfixNotationElement> elements = new ArrayList<PostfixNotationElement>();

        LogicalExpression logExp = boolExp.getLogExp();
        elements.addAll(parseLogicalExpression(logExp));

        List<Part<BooleanOp, LogicalExpression>> rightPart = boolExp.rightPart();
        if (!rightPart.isEmpty()) {
            for (Part<BooleanOp, LogicalExpression> part : rightPart) {
                elements.addAll(parseLogicalExpression(part.getExpression()));
                elements.add(new PostfixNotationElement(MATH_OPERATION, part.operation));
            }
        }

        return elements;
    }

    private static List<PostfixNotationElement> parseLogicalExpression(LogicalExpression exp){
        List<PostfixNotationElement> elements = new ArrayList<PostfixNotationElement>();

        CompareExpression cmprExp = exp.getCmprExp();
        elements.addAll(parseCompareExpression(cmprExp));

        List<Part<LogicalOp, CompareExpression>> rightPart = exp.rightPart();
        if(!rightPart.isEmpty()){
            for (Part<LogicalOp, CompareExpression> part : rightPart) {
                elements.addAll(parseCompareExpression(part.getExpression()));
                elements.add(new PostfixNotationElement(MATH_OPERATION, part.operation));
            }
        }

        return elements;
    }

    private static List<PostfixNotationElement> parseCompareExpression(CompareExpression exp){
        List<PostfixNotationElement> elements = new ArrayList<PostfixNotationElement>();

        ArithmeticalAddExpression arrAddExp = exp.getArAddExp();
        elements.addAll(parseArithmeticalAddExpression(arrAddExp));

        List<Part<CompareOp, ArithmeticalAddExpression>> rightPart = exp.rightPart();
        if(!rightPart.isEmpty()){
            for (Part<CompareOp, ArithmeticalAddExpression> part : rightPart) {
                elements.addAll(parseArithmeticalAddExpression(part.getExpression()));
                elements.add(new PostfixNotationElement(MATH_OPERATION, part.operation));
            }
        }

        return elements;
    }

    private static List<PostfixNotationElement> parseArithmeticalAddExpression(
            ArithmeticalAddExpression exp){

        List<PostfixNotationElement> elements = new ArrayList<PostfixNotationElement>();

        ArithmeticalMullExpression arMullExp = exp.getArMullExp();
        elements.addAll(parseArithmeticalMullExpression(arMullExp));

        List<Part<ArithmeticalAddOp, ArithmeticalMullExpression>> rightPart = exp.rightPart();
        if(!rightPart.isEmpty()){
            for (Part<ArithmeticalAddOp, ArithmeticalMullExpression> part : rightPart) {
                elements.addAll(parseArithmeticalMullExpression(part.getExpression()));
                elements.add(new PostfixNotationElement(MATH_OPERATION, part.operation));
            }
        }

        return elements;
    }

    private static List<PostfixNotationElement> parseArithmeticalMullExpression(
            ArithmeticalMullExpression exp){

        List<PostfixNotationElement> elements = new ArrayList<PostfixNotationElement>();

        PrefixExpression prefxExp = exp.getPrefxExp();
        elements.addAll(parsePrefixExpression(prefxExp));

        List<Part<ArithmeticalMullOp, PrefixExpression>> rightPart = exp.rightPart();
        if(!rightPart.isEmpty()){
            for (Part<ArithmeticalMullOp, PrefixExpression> part : rightPart) {
                elements.addAll(parsePrefixExpression(part.getExpression()));
                elements.add(new PostfixNotationElement(MATH_OPERATION, part.operation));
            }
        }

        return elements;
    }

    private static List<PostfixNotationElement> parsePrefixExpression(
            PrefixExpression exp){

        List<PostfixNotationElement> elements = new ArrayList<PostfixNotationElement>();

        PostfixExpression postfixExpression = exp.getPostfixExpression();
        elements.add(new PostfixNotationElement(POSTFIX_EXPRESSION, postfixExpression));

        PrefixOp prefixOp = exp.getPrefixOp();
        if (prefixOp != null){
            elements.add(new PostfixNotationElement(MATH_OPERATION, prefixOp));
        }

        return elements;
    }

}
